package com.jctrip.hotel.route.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.google.common.collect.Lists;
import com.jctrip.hotel.common.enums.SourceEnum;
import com.jctrip.hotel.common.util.BeanUtil;
import com.jctrip.hotel.common.util.DateUtil;
import com.jctrip.hotel.common.util.JsonUtil;
import com.jctrip.hotel.domain.dao.*;
import com.jctrip.hotel.domain.entity.*;
import com.jctrip.hotel.route.model.param.GetHotelRoomStatusParam;
import com.jctrip.hotel.route.model.result.GetHotelRoomStatusResult;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 获取酒店房态接口-服务
 */
@Service
public class GetHotelRoomStatusService extends RouteService {

    @Autowired
    private HtPriceCalendarDao htPriceCalendarDao;

    @Autowired
    private HtPricePolicyRangeDao htPricePolicyRangeDao;

    @Autowired
    private HtPriceCalendarCnbeDao htPriceCalendarCnbeDao;

    @Autowired
    private HtPriceCalendarRezenDao htPriceCalendarRezenDao;

    @Autowired
    private HtPriceCalendarAtourDao htPriceCalendarAtourDao;
    @Autowired
    private HtPriceCalendarHmeinDao htPriceCalendarHmeinDao;

    @Autowired
    private HtPriceCalendarGreentreeDao htPriceCalendarGreentreeDao;
    @Autowired
    private HtPriceCalendarElongDao htPriceCalendarElongDao;
    @Autowired
    private HtPriceCalendarElongProductDao htPriceCalendarElongProductDao;
    @Autowired
    private HtPriceCalendarHworldDao htPriceCalendarHworldDao;
    @Autowired
    private HtHotelMappingDao htHotelMappingDao;
    @Autowired
    private HtRoomTypeMappingDao htRoomTypeMappingDao;

    public GetHotelRoomStatusResult execute(GetHotelRoomStatusParam param) {
        int maxDays = 90;
        if (param.getDays() > maxDays) {
            throw new RuntimeException("days不能大于" + maxDays);
        }
        // 价格政策配置
        List<HtPricePolicy> htPricePolicies = getPricePolicies();
        List<String> sourceCodes = htPricePolicies.stream()
                .map(HtPricePolicy::getSourceCode)
                .collect(Collectors.toList());
        // 酒店标准信息
        HtHotelStdInfo htHotelStdInfo = getHotelStdInfo(param.getHotelId(), sourceCodes);
        if (htHotelStdInfo == null) {
            throw new RuntimeException("无效的hotelId");
        }
        // 找出需要映射的rp
        List<HtPricePolicy> tempHtPricePolicies = htPricePolicies.stream()
                .filter(e -> e.getSourceCode().equals(htHotelStdInfo.getSourceCode()))
                .filter(e -> (StringUtils.isNotBlank(e.getExtra()) && Objects.nonNull(JsonUtil.toMap(e.getExtra()).get("mappingFlag"))))
                .collect(Collectors.toList());
        // 找出正常rp
        htPricePolicies = htPricePolicies.stream()
                .filter(e -> e.getSourceCode().equals(htHotelStdInfo.getSourceCode()))
                .filter(e -> !(StringUtils.isNotBlank(e.getExtra()) && Objects.nonNull(JsonUtil.toMap(e.getExtra()).get("mappingFlag"))))
                .collect(Collectors.toList());
        List<String> pricePolicyIds = htPricePolicies.stream()
                .map(HtPricePolicy::getPricePolicyId)
                .collect(Collectors.toList());
        // 价格政策区间配置
        List<HtPricePolicyRange> htPricePolicyRanges = getPricePolicyRanges(pricePolicyIds);
        Map<String, List<HtPricePolicyRange>> htPricePolicyRangeMap = htPricePolicyRanges.stream()
                .collect(Collectors.groupingBy(HtPricePolicyRange::getPricePolicyId));
        Date beginDate = DateUtil.parseDate(param.getBeginDate(), "yyyy-MM-dd");
        Date endDate = DateUtils.addDays(beginDate, param.getDays() - 1);
        final List<HtPricePolicy> htPricePoliciesFinal = htPricePolicies;
        // todo 龙腾港澳酒店处理
        if (SourceEnum.CNBE.getCode().equals(htHotelStdInfo.getSourceCode()) || SourceEnum.CNBEINT.getCode().equals(htHotelStdInfo.getSourceCode())) {
            return getCnbeRoomsStatus(param, beginDate, endDate, htPricePoliciesFinal, htPricePolicyRangeMap);
        }
        // 丽呈酒店处理，二方+三方
        if (SourceEnum.REZEN.getCode().equals(htHotelStdInfo.getSourceCode()) || SourceEnum.REZENCO.getCode().equals(htHotelStdInfo.getSourceCode())) {
            return getRezenRoomsStatus(param, beginDate, endDate, htPricePoliciesFinal, htPricePolicyRangeMap);
        }
        // 亚朵酒店处理
        if (SourceEnum.ATOUR.getCode().equals(htHotelStdInfo.getSourceCode())) {
            return getAtourRoomsStatus(param, beginDate, endDate, htPricePoliciesFinal, htPricePolicyRangeMap);
        }
        // 格林酒店处理
        if (SourceEnum.GREENTREE.getCode().equals(htHotelStdInfo.getSourceCode())) {
            return getRoomsStatusGreentree(param, beginDate, endDate, htPricePoliciesFinal, htPricePolicyRangeMap);
        }
        // 艺龙特殊处理
        if (SourceEnum.ELONGDOM.getCode().equals(htHotelStdInfo.getSourceCode())) {
            return getRoomStatusListElongDom(param, beginDate, endDate, htPricePoliciesFinal, htPricePolicyRangeMap);
        }
        // 如家酒店处理
        if (SourceEnum.HMEIN.getCode().equals(htHotelStdInfo.getSourceCode())) {
            return getRoomsStatusHmein(param, beginDate, endDate, htPricePoliciesFinal, htPricePolicyRangeMap);
        }
        // 华住酒店处理
        if (SourceEnum.HWORLD.getCode().equals(htHotelStdInfo.getSourceCode())) {
            return getRoomsStatusHworld(param, beginDate, endDate, htPricePoliciesFinal, htPricePolicyRangeMap);
        }

        // 价格日历
        List<HtPriceCalendar> htPriceCalendars =
                getPriceCalenders(param.getHotelId(), param.getRoomTypeCode(), beginDate, endDate);
        Map<String, List<HtPriceCalendar>> htPriceCalendarMap = htPriceCalendars.stream()
                .collect(Collectors.groupingBy(HtPriceCalendar::getRoomTypeCode));
        // 组装成房态数据
        List<GetHotelRoomStatusResult.RoomType> roomTypeList = htPriceCalendarMap.keySet().stream()
                .map(roomTypeCode -> {
                    String roomTypeName = htPriceCalendarMap.get(roomTypeCode).stream()
                            .map(HtPriceCalendar::getRoomTypeName)
                            .findAny()
                            .orElse(null);
                    List<GetHotelRoomStatusResult.RoomStatus> roomStatusList =
                            getRoomStatusList(param.getHotelId(), htPriceCalendarMap.get(roomTypeCode), htPricePoliciesFinal, htPricePolicyRangeMap);
                    return GetHotelRoomStatusResult.RoomType.builder()
                            .roomTypeCode(roomTypeCode)
                            .roomTypeName(roomTypeName)
                            .roomStatusList(roomStatusList)
                            .build();
                })
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(tempHtPricePolicies)) {
            Map<String, List<HtPricePolicy>> mappingSourceMap = tempHtPricePolicies.stream()
                    .collect(Collectors.groupingBy(e -> (String) JsonUtil.toMap(e.getExtra()).get("mappingSource")));
            for (String mappingSource : mappingSourceMap.keySet()) {
                List<HtPricePolicy> pricePolicies = mappingSourceMap.get(mappingSource);
                List<HtPricePolicyRange> pricePolicyRanges = getPricePolicyRanges(pricePolicies.stream()
                        .map(HtPricePolicy::getPricePolicyId)
                        .collect(Collectors.toList()));
                Map<String, List<HtPricePolicyRange>> policyRangesMap = pricePolicyRanges.stream()
                        .collect(Collectors.groupingBy(HtPricePolicyRange::getPricePolicyId));
                roomTypeList = getRoomsStatus(roomTypeList, mappingSource, param, beginDate, endDate, pricePolicies, policyRangesMap);
            }

        }
        return GetHotelRoomStatusResult.builder()
                .hotelId(param.getHotelId())
                .roomTypeList(roomTypeList)
                .build();
    }

    private List<GetHotelRoomStatusResult.RoomType> getRoomsStatus(List<GetHotelRoomStatusResult.RoomType> roomTypeList,
                                                                   String sourceCode,
                                                                   GetHotelRoomStatusParam param,
                                                                   Date beginDate, Date endDate,
                                                                   List<HtPricePolicy> pricePolicies,
                                                                   Map<String, List<HtPricePolicyRange>> htPricePolicyRangeMap) {
        String sourceHotelId = param.getHotelId();
        String sourceRoomTypeCode = param.getRoomTypeCode();
        transformParam(param);
        if (sourceHotelId.equals(param.getHotelId())) {
            return roomTypeList;
        }
        List<HtRoomTypeMapping> roomTypeMappings = selectRoomTypeMapping(sourceHotelId);
        if (CollectionUtils.isEmpty(roomTypeMappings)) {
            return roomTypeList;
        }
        if (StringUtils.isNotBlank(sourceRoomTypeCode)) {
            HtRoomTypeMapping htRoomTypeMapping =
                    roomTypeMappings.stream().filter(e -> e.getRoomTypeCode().equals(sourceRoomTypeCode)).findFirst().orElse(null);
            if (Objects.isNull(htRoomTypeMapping)) {
                param.setHotelId(sourceHotelId);
                param.setRoomTypeCode(sourceRoomTypeCode);
                return roomTypeList;
            }
            param.setRoomTypeCode(htRoomTypeMapping.getMappingRoomTypeCode());
        }
        SourceEnum sourceEnum = SourceEnum.getByCode(sourceCode);
        GetHotelRoomStatusResult tempRoomStatusResult = null;
        if (Objects.isNull(sourceEnum)) {
            return roomTypeList;
        }
        switch (sourceEnum) {
            case CNBE:
            case CNBEINT:
                tempRoomStatusResult = getCnbeRoomsStatus(param, beginDate, endDate, pricePolicies, htPricePolicyRangeMap);
                break;
            case REZEN:
                tempRoomStatusResult = getRezenRoomsStatus(param, beginDate, endDate, pricePolicies, htPricePolicyRangeMap);
                break;
            case ATOUR:
                tempRoomStatusResult = getAtourRoomsStatus(param, beginDate, endDate, pricePolicies, htPricePolicyRangeMap);
                break;
            case GREENTREE:
                tempRoomStatusResult = getRoomsStatusGreentree(param, beginDate, endDate, pricePolicies, htPricePolicyRangeMap);
                break;
            case ELONGDOM:
                tempRoomStatusResult = getRoomStatusListElongDom(param, beginDate, endDate, pricePolicies, htPricePolicyRangeMap);
                break;
            case HMEIN:
                tempRoomStatusResult = getRoomsStatusHmein(param, beginDate, endDate, pricePolicies, htPricePolicyRangeMap);
                break;
            case HWORLD:
                tempRoomStatusResult = getRoomsStatusHworld(param, beginDate, endDate, pricePolicies, htPricePolicyRangeMap);
                break;
            default:
                break;
        }
        param.setHotelId(sourceHotelId);
        param.setRoomTypeCode(sourceRoomTypeCode);
        if (Objects.isNull(tempRoomStatusResult)) {
            return roomTypeList;
        }
        List<GetHotelRoomStatusResult.RoomType> roomTypes = tempRoomStatusResult.getRoomTypeList();
        return roomTypeList.stream().map(e -> {

            HtRoomTypeMapping htRoomTypeMapping =
                    roomTypeMappings.stream().filter(f -> f.getRoomTypeCode().equals(e.getRoomTypeCode())).findFirst().orElse(null);
            if (Objects.isNull(htRoomTypeMapping)) {
                return GetHotelRoomStatusResult.RoomType.builder()
                        .roomTypeCode(e.getRoomTypeCode())
                        .roomTypeName(e.getRoomTypeCode())
                        .roomStatusList(e.getRoomStatusList())
                        .build();
            }

            List<GetHotelRoomStatusResult.RoomStatus> roomStatusList =
                    roomTypes.stream().filter(f -> htRoomTypeMapping.getMappingRoomTypeCode().equals(f.getRoomTypeCode()))
                            .findFirst()
                            .orElse(GetHotelRoomStatusResult.RoomType.builder().build())
                            .getRoomStatusList();
            if (CollectionUtils.isNotEmpty(roomStatusList)) {
                e.getRoomStatusList().addAll(roomStatusList);
            }

            return GetHotelRoomStatusResult.RoomType.builder()
                    .roomTypeCode(e.getRoomTypeCode())
                    .roomTypeName(e.getRoomTypeCode())
                    .roomStatusList(e.getRoomStatusList())
                    .build();
        }).collect(Collectors.toList());
    }

    private void transformParam(GetHotelRoomStatusParam param) {
        LambdaQueryWrapper<HtHotelMapping> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(HtHotelMapping::getHotelId, param.getHotelId());
        HtHotelMapping htHotelMapping = htHotelMappingDao.selectOne(wrapper);
        if (Objects.isNull(htHotelMapping)) {
            return;
        }
        param.setHotelId(htHotelMapping.getMappingHotelId());
    }

    private List<HtRoomTypeMapping> selectRoomTypeMapping(String hotelId) {
        LambdaQueryWrapper<HtRoomTypeMapping> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(HtRoomTypeMapping::getHotelId, hotelId);
        return htRoomTypeMappingDao.selectList(wrapper);
    }

    private GetHotelRoomStatusResult getCnbeRoomsStatus(GetHotelRoomStatusParam param,
                                                        Date beginDate, Date endDate,
                                                        List<HtPricePolicy> pricePolicies,
                                                        Map<String, List<HtPricePolicyRange>> htPricePolicyRangeMap) {
        List<HtPriceCalendarCnbe> htPriceCalendarCnbes =
                getCNBEPriceCalenders(param.getHotelId(), param.getRoomTypeCode(), beginDate, endDate);
        Map<String, List<HtPriceCalendarCnbe>> htCnbePriceCalendarMap = htPriceCalendarCnbes.stream()
                .collect(Collectors.groupingBy(HtPriceCalendarCnbe::getRoomTypeCode));
        List<GetHotelRoomStatusResult.RoomType> roomTypeList = htCnbePriceCalendarMap.keySet()
                .stream().map(roomTypeCode -> {
                    String roomTypeName = htCnbePriceCalendarMap.get(roomTypeCode).stream()
                            .map(HtPriceCalendarCnbe::getRoomTypeName).findAny().orElse(null);
                    List<GetHotelRoomStatusResult.RoomStatus> roomStatusList =
                            getCNBERoomstatusList(htCnbePriceCalendarMap.get(roomTypeCode),
                                    pricePolicies, htPricePolicyRangeMap);
                    return GetHotelRoomStatusResult.RoomType.builder()
                            .roomTypeCode(roomTypeCode)
                            .roomTypeName(roomTypeName)
                            .roomStatusList(roomStatusList)
                            .build();
                }).collect(Collectors.toList());
        return GetHotelRoomStatusResult.builder()
                .hotelId(param.getHotelId())
                .roomTypeList(roomTypeList)
                .build();
    }

    private GetHotelRoomStatusResult getRezenRoomsStatus(GetHotelRoomStatusParam param,
                                                         Date beginDate, Date endDate,
                                                         List<HtPricePolicy> pricePolicies,
                                                         Map<String, List<HtPricePolicyRange>> htPricePolicyRangeMap) {
        List<HtPriceCalendarRezen> htPriceCalendarRezens =
                getRezenPriceCalenders(param.getHotelId(), param.getRoomTypeCode(), beginDate, endDate);
        // 根据房型区分
        Map<String, List<HtPriceCalendarRezen>> htRezenPriceCalendarMap = htPriceCalendarRezens.stream()
                .collect(Collectors.groupingBy(HtPriceCalendarRezen::getRoomTypeCode));

        List<GetHotelRoomStatusResult.RoomType> roomTypeList = htRezenPriceCalendarMap.keySet()
                .stream().map(roomTypeCode -> {
                    String roomTypeName = htRezenPriceCalendarMap.get(roomTypeCode).stream()
                            .map(HtPriceCalendarRezen::getRoomTypeName).findAny().orElse(null);
                    List<GetHotelRoomStatusResult.RoomStatus> roomStatusList =
                            getRezenRoomstatusList(htRezenPriceCalendarMap.get(roomTypeCode),
                                    pricePolicies, htPricePolicyRangeMap);
                    return GetHotelRoomStatusResult.RoomType.builder()
                            .roomTypeCode(roomTypeCode)
                            .roomTypeName(roomTypeName)
                            .roomStatusList(roomStatusList)
                            .build();
                }).collect(Collectors.toList());
        return GetHotelRoomStatusResult.builder()
                .hotelId(param.getHotelId())
                .roomTypeList(roomTypeList)
                .build();
    }

    private GetHotelRoomStatusResult getAtourRoomsStatus(GetHotelRoomStatusParam param,
                                                         Date beginDate, Date endDate,
                                                         List<HtPricePolicy> pricePolicies,
                                                         Map<String, List<HtPricePolicyRange>> htPricePolicyRangeMap) {
        List<HtPriceCalendarAtour> htPriceCalendarAtours =
                getAtourPriceCalenders(param.getHotelId(), param.getRoomTypeCode(), beginDate, endDate);
        // 根据房型区分
        Map<String, List<HtPriceCalendarAtour>> htAtourPriceCalendarMap = htPriceCalendarAtours.stream()
                .collect(Collectors.groupingBy(HtPriceCalendarAtour::getRoomTypeCode));

        List<GetHotelRoomStatusResult.RoomType> roomTypeList = htAtourPriceCalendarMap.keySet()
                .stream().map(roomTypeCode -> {
                    String roomTypeName = htAtourPriceCalendarMap.get(roomTypeCode).stream()
                            .map(HtPriceCalendarAtour::getRoomTypeName).findAny().orElse(null);
                    List<GetHotelRoomStatusResult.RoomStatus> roomStatusList =
                            getAtourRoomstatusList(htAtourPriceCalendarMap.get(roomTypeCode),
                                    pricePolicies, htPricePolicyRangeMap);
                    return GetHotelRoomStatusResult.RoomType.builder()
                            .roomTypeCode(roomTypeCode)
                            .roomTypeName(roomTypeName)
                            .roomStatusList(roomStatusList)
                            .build();
                }).collect(Collectors.toList());
        return GetHotelRoomStatusResult.builder()
                .hotelId(param.getHotelId())
                .roomTypeList(roomTypeList)
                .build();
    }

    private GetHotelRoomStatusResult getRoomsStatusHworld(GetHotelRoomStatusParam param,
                                                          Date beginDate, Date endDate,
                                                          List<HtPricePolicy> pricePolicies,
                                                          Map<String, List<HtPricePolicyRange>> htPricePolicyRangeMap) {
        List<HtPriceCalendarHworld> priceCalendersHworld =
                getPriceCalendersHworld(param.getHotelId(), param.getRoomTypeCode(), beginDate, endDate);
        // 根据房型区分
        Map<String, List<HtPriceCalendarHworld>> htPriceCalendarMapHworld = priceCalendersHworld.stream()
                .collect(Collectors.groupingBy(HtPriceCalendarHworld::getRoomTypeCode));
        List<GetHotelRoomStatusResult.RoomType> roomTypeList = htPriceCalendarMapHworld.keySet()
                .stream().map(roomTypeCode -> {
                    String roomTypeName = htPriceCalendarMapHworld.get(roomTypeCode).stream()
                            .map(HtPriceCalendarHworld::getRoomTypeName).findAny().orElse(null);
                    List<GetHotelRoomStatusResult.RoomStatus> roomStatusList =
                            getRoomstatusListHworld(htPriceCalendarMapHworld.get(roomTypeCode),
                                    pricePolicies, htPricePolicyRangeMap);
                    return GetHotelRoomStatusResult.RoomType.builder()
                            .roomTypeCode(roomTypeCode)
                            .roomTypeName(roomTypeName)
                            .roomStatusList(roomStatusList)
                            .build();
                }).collect(Collectors.toList());
        return GetHotelRoomStatusResult.builder()
                .hotelId(param.getHotelId())
                .roomTypeList(roomTypeList)
                .build();
    }

    private GetHotelRoomStatusResult getRoomsStatusHmein(GetHotelRoomStatusParam param,
                                                         Date beginDate, Date endDate,
                                                         List<HtPricePolicy> pricePolicies,
                                                         Map<String, List<HtPricePolicyRange>> htPricePolicyRangeMap) {
        List<HtPriceCalendarHmein> priceCalendersHmein =
                getPriceCalendersHmein(param.getHotelId(), param.getRoomTypeCode(), beginDate, endDate);
        // 根据房型区分
        Map<String, List<HtPriceCalendarHmein>> htPriceCalendarMapHmein = priceCalendersHmein.stream()
                .collect(Collectors.groupingBy(HtPriceCalendarHmein::getRoomTypeCode));
        List<GetHotelRoomStatusResult.RoomType> roomTypeList = htPriceCalendarMapHmein.keySet()
                .stream().map(roomTypeCode -> {
                    String roomTypeName = htPriceCalendarMapHmein.get(roomTypeCode).stream()
                            .map(HtPriceCalendarHmein::getRoomTypeName).findAny().orElse(null);
                    List<GetHotelRoomStatusResult.RoomStatus> roomStatusList =
                            getRoomstatusListHmein(htPriceCalendarMapHmein.get(roomTypeCode),
                                    pricePolicies, htPricePolicyRangeMap);
                    return GetHotelRoomStatusResult.RoomType.builder()
                            .roomTypeCode(roomTypeCode)
                            .roomTypeName(roomTypeName)
                            .roomStatusList(roomStatusList)
                            .build();
                }).collect(Collectors.toList());
        return GetHotelRoomStatusResult.builder()
                .hotelId(param.getHotelId())
                .roomTypeList(roomTypeList)
                .build();
    }

    private GetHotelRoomStatusResult getRoomsStatusGreentree(GetHotelRoomStatusParam param,
                                                             Date beginDate, Date endDate,
                                                             List<HtPricePolicy> pricePolicies,
                                                             Map<String, List<HtPricePolicyRange>> htPricePolicyRangeMap) {
        List<HtPriceCalendarGreentree> htPriceCalendarGreentrees =
                getPriceCalendersGreentree(param.getHotelId(), param.getRoomTypeCode(), beginDate, endDate);
        // 根据房型区分
        Map<String, List<HtPriceCalendarGreentree>> htPriceCalendarMapGreentree = htPriceCalendarGreentrees.stream()
                .collect(Collectors.groupingBy(HtPriceCalendarGreentree::getRoomTypeCode));
        List<GetHotelRoomStatusResult.RoomType> roomTypeList = htPriceCalendarMapGreentree.keySet()
                .stream().map(roomTypeCode -> {
                    String roomTypeName = htPriceCalendarMapGreentree.get(roomTypeCode).stream()
                            .map(HtPriceCalendarGreentree::getRoomTypeName).findAny().orElse(null);
                    List<GetHotelRoomStatusResult.RoomStatus> roomStatusList =
                            getRoomstatusListGreentree(htPriceCalendarMapGreentree.get(roomTypeCode),
                                    pricePolicies, htPricePolicyRangeMap);
                    return GetHotelRoomStatusResult.RoomType.builder()
                            .roomTypeCode(roomTypeCode)
                            .roomTypeName(roomTypeName)
                            .roomStatusList(roomStatusList)
                            .build();
                }).collect(Collectors.toList());
        return GetHotelRoomStatusResult.builder()
                .hotelId(param.getHotelId())
                .roomTypeList(roomTypeList)
                .build();
    }

    private GetHotelRoomStatusResult getRoomStatusListElongDom(GetHotelRoomStatusParam param,
                                                               Date beginDate, Date endDate,
                                                               List<HtPricePolicy> pricePolicies,
                                                               Map<String, List<HtPricePolicyRange>> htPricePolicyRangeMap) {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        String date = format.format(new Date());
        List<HtPriceCalendarElongProduct> htPriceCalendarElongProducts = htPriceCalendarElongProductDao.selectList(
                new LambdaQueryWrapper<HtPriceCalendarElongProduct>()
                        .eq(HtPriceCalendarElongProduct::getHotelId, param.getHotelId())
                        .eq(StringUtils.isNotBlank(param.getRoomTypeCode()),
                                HtPriceCalendarElongProduct::getRoomTypeCode, param.getRoomTypeCode())
                        .between(HtPriceCalendarElongProduct::getCalDate, date, date));
        if (CollectionUtils.isEmpty(htPriceCalendarElongProducts)) {
            return null;
        }
        List<HtPriceCalendarElong> htPriceCalendarElongs =
                getPriceCalendersElong(param.getHotelId(), param.getRoomTypeCode(), beginDate, endDate, htPriceCalendarElongProducts);
        // 根据房型区分
        Map<String, List<HtPriceCalendarElong>> htPriceCalendarMapElong = htPriceCalendarElongs.stream()
                .collect(Collectors.groupingBy(HtPriceCalendarElong::getRoomTypeCode));

        List<GetHotelRoomStatusResult.RoomType> roomTypeList = htPriceCalendarMapElong.keySet()
                .stream().map(roomTypeCode -> {
                    String roomTypeName = htPriceCalendarMapElong.get(roomTypeCode).stream()
                            .map(HtPriceCalendarElong::getRoomTypeName).findAny().orElse(null);
                    List<GetHotelRoomStatusResult.RoomStatus> roomStatusList =
                            getRoomstatusElongList(htPriceCalendarMapElong.get(roomTypeCode), pricePolicies,
                                    htPricePolicyRangeMap, htPriceCalendarElongProducts);
                    return GetHotelRoomStatusResult.RoomType.builder()
                            .roomTypeCode(roomTypeCode)
                            .roomTypeName(roomTypeName)
                            .roomStatusList(roomStatusList)
                            .build();
                }).collect(Collectors.toList());
        return GetHotelRoomStatusResult.builder()
                .hotelId(param.getHotelId())
                .roomTypeList(roomTypeList)
                .build();
    }

    private List<HtPricePolicyRange> getPricePolicyRanges(List<String> pricePolicyIds) {
        LambdaQueryWrapper<HtPricePolicyRange> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(HtPricePolicyRange::getPricePolicyId, pricePolicyIds);
        return htPricePolicyRangeDao.selectList(queryWrapper);
    }

    private List<HtPriceCalendarElong> getPriceCalendersElong(String hotelId, String roomTypeCode,
                                                              Date beginDate, Date endDate,
                                                              List<HtPriceCalendarElongProduct> htPriceCalendarElongProducts) {
        LambdaQueryWrapper<HtPriceCalendarElong> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(HtPriceCalendarElong::getHotelId, hotelId);
        if (StringUtils.isNotBlank(roomTypeCode)) {
            queryWrapper.eq(HtPriceCalendarElong::getRoomTypeCode, roomTypeCode);
        }
        queryWrapper.between(HtPriceCalendarElong::getCalDate, beginDate, endDate);
        List<HtPriceCalendarElong> htPriceCalendarElongs = htPriceCalendarElongDao.selectList(queryWrapper);

        return htPriceCalendarElongProducts.stream()
                .flatMap(htPriceCalendarElongProduct -> htPriceCalendarElongs.stream()
                        .filter(htPriceCalendarElong -> {
                            return htPriceCalendarElong.getHotelId().equals(htPriceCalendarElongProduct.getHotelId())
                                    && htPriceCalendarElong.getRoomTypeCode().equals(htPriceCalendarElongProduct.getRoomTypeCode())
                                    && htPriceCalendarElong.getProductId().equals(htPriceCalendarElongProduct.getProductId());
                        }))
                .distinct()
                .collect(Collectors.toList());
    }

    private List<HtPriceCalendarRezen> getRezenPriceCalenders(String hotelId, String roomTypeCode,
                                                              Date beginDate, Date endDate) {
        LambdaQueryWrapper<HtPriceCalendarRezen> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(HtPriceCalendarRezen::getHotelId, hotelId);
        if (StringUtils.isNotBlank(roomTypeCode)) {
            queryWrapper.eq(HtPriceCalendarRezen::getRoomTypeCode, roomTypeCode);
        }
        queryWrapper.between(HtPriceCalendarRezen::getCalDate, beginDate, endDate);
        return htPriceCalendarRezenDao.selectList(queryWrapper);
    }

    private List<HtPriceCalendarAtour> getAtourPriceCalenders(String hotelId, String roomTypeCode,
                                                              Date beginDate, Date endDate) {
        LambdaQueryWrapper<HtPriceCalendarAtour> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(HtPriceCalendarAtour::getHotelId, hotelId);
        if (StringUtils.isNotBlank(roomTypeCode)) {
            queryWrapper.eq(HtPriceCalendarAtour::getRoomTypeCode, roomTypeCode);
        }
        queryWrapper.between(HtPriceCalendarAtour::getCalDate, beginDate, endDate);
        return htPriceCalendarAtourDao.selectList(queryWrapper);
    }

    private List<HtPriceCalendarHworld> getPriceCalendersHworld(String hotelId, String roomTypeCode,
                                                                Date beginDate, Date endDate) {
        return htPriceCalendarHworldDao.selectList(new LambdaQueryWrapper<HtPriceCalendarHworld>()
                .eq(HtPriceCalendarHworld::getHotelId, hotelId)
                .eq(StringUtils.isNotBlank(roomTypeCode), HtPriceCalendarHworld::getRoomTypeCode, roomTypeCode)
                .between(HtPriceCalendarHworld::getCalDate, beginDate, endDate));
    }

    private List<HtPriceCalendarHmein> getPriceCalendersHmein(String hotelId, String roomTypeCode,
                                                              Date beginDate, Date endDate) {
        return htPriceCalendarHmeinDao.selectList(new LambdaQueryWrapper<HtPriceCalendarHmein>()
                .eq(HtPriceCalendarHmein::getHotelId, hotelId)
                .eq(StringUtils.isNotBlank(roomTypeCode), HtPriceCalendarHmein::getRoomTypeCode, roomTypeCode)
                .between(HtPriceCalendarHmein::getCalDate, beginDate, endDate));
    }

    private List<HtPriceCalendarGreentree> getPriceCalendersGreentree(String hotelId, String roomTypeCode,
                                                                      Date beginDate, Date endDate) {
        LambdaQueryWrapper<HtPriceCalendarGreentree> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(HtPriceCalendarGreentree::getHotelId, hotelId);
        if (StringUtils.isNotBlank(roomTypeCode)) {
            queryWrapper.eq(HtPriceCalendarGreentree::getRoomTypeCode, roomTypeCode);
        }
        queryWrapper.between(HtPriceCalendarGreentree::getCalDate, beginDate, endDate);
        return htPriceCalendarGreentreeDao.selectList(queryWrapper);
    }

    private List<HtPriceCalendarCnbe> getCNBEPriceCalenders(String hotelId, String roomTypeCode, Date beginDate, Date endDate) {
        LambdaQueryWrapper<HtPriceCalendarCnbe> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(HtPriceCalendarCnbe::getHotelId, hotelId);
        if (StringUtils.isNotBlank(roomTypeCode)) {
            queryWrapper.eq(HtPriceCalendarCnbe::getRoomTypeCode, roomTypeCode);
        }
        queryWrapper.between(HtPriceCalendarCnbe::getCalDate, beginDate, endDate);
        return htPriceCalendarCnbeDao.selectList(queryWrapper);
    }

    private List<HtPriceCalendar> getPriceCalenders(String hotelId, String roomTypeCode, Date beginDate, Date endDate) {
        LambdaQueryWrapper<HtPriceCalendar> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(HtPriceCalendar::getHotelId, hotelId);
        if (StringUtils.isNotBlank(roomTypeCode)) {
            queryWrapper.eq(HtPriceCalendar::getRoomTypeCode, roomTypeCode);
        }
        queryWrapper.between(HtPriceCalendar::getCalDate, beginDate, endDate);
        return htPriceCalendarDao.selectList(queryWrapper);
    }

    /**
     * 获取房态列表（list条数 = 房价日历数 * 价格政策数）
     */
    private List<GetHotelRoomStatusResult.RoomStatus> getRoomStatusList(String hotelId, List<HtPriceCalendar> htPriceCalendars,
                                                                        List<HtPricePolicy> htPricePolicies,
                                                                        Map<String, List<HtPricePolicyRange>> htPricePolicyRangeMap) {
        return htPriceCalendars.stream()
                .flatMap(htPriceCalendar -> htPricePolicies.stream()
                        .map(htPricePolicy -> {
                            GetHotelRoomStatusResult.RoomStatus roomStatus
                                    = GetHotelRoomStatusResult.RoomStatus.builder().build();
                            BeanUtil.copyProperties(htPriceCalendar, roomStatus);
                            List<HtPricePolicyRange> htPricePolicyRanges
                                    = htPricePolicyRangeMap.get(htPricePolicy.getPricePolicyId());
                            updateRoomStatus(hotelId, roomStatus, htPricePolicy, htPricePolicyRanges);
                            // 莹旅 特殊处理 库存小于5变0
                            if ("bjsyinglv".equals(htPricePolicy.getRouteId()) && roomStatus.getQuota() < 5) {
                                roomStatus.setQuota(0);
                            }
                            return roomStatus;
                        }))
                .sorted(Comparator.comparing(GetHotelRoomStatusResult.RoomStatus::getPricePolicyCode)
                        .thenComparing(GetHotelRoomStatusResult.RoomStatus::getCalDate))
                .collect(Collectors.toList());
    }

    private List<GetHotelRoomStatusResult.RoomStatus> getRoomstatusElongList(List<HtPriceCalendarElong> htPriceCalendarElongs,
                                                                             List<HtPricePolicy> htPricePolicies,
                                                                             Map<String, List<HtPricePolicyRange>> htPricePolicyRangeMap,
                                                                             List<HtPriceCalendarElongProduct> htPriceCalendarElongProducts) {
        List<GetHotelRoomStatusResult.RoomStatus> list = Lists.newArrayList();
        htPricePolicies.forEach(htPricePolicy -> {
            htPriceCalendarElongProducts.stream()
                    .filter(htPriceCalendarElongProduct ->
                            htPriceCalendarElongProduct.getProductCode().equals(htPricePolicy.getProductCode()))
                    .forEach(htPriceCalendarElongProduct -> {
                        list.addAll(htPriceCalendarElongs.stream().filter(htPriceCalendarElong ->
                                htPriceCalendarElongProduct.getProductId().equals(htPriceCalendarElong.getProductId()))
                                .map(htPriceCalendarElong -> {
                                    GetHotelRoomStatusResult.RoomStatus roomStatus = GetHotelRoomStatusResult
                                            .RoomStatus.builder().build();
                                    BeanUtil.copyProperties(htPriceCalendarElong, roomStatus);
                                    List<HtPricePolicyRange> htPricePolicyRanges
                                            = htPricePolicyRangeMap.get(htPricePolicy.getPricePolicyId());
                                    updateRoomStatus(null, roomStatus, htPricePolicy, htPricePolicyRanges);
                                    if (StringUtils.isBlank(roomStatus.getCancelRule())) {
                                        roomStatus.setCancelRule("不可取消");
                                    }
                                    return roomStatus;
                                })
                                .sorted(Comparator.comparing(GetHotelRoomStatusResult.RoomStatus::getPricePolicyCode)
                                        .thenComparing(GetHotelRoomStatusResult.RoomStatus::getBreakfastCount)
                                        .thenComparing(GetHotelRoomStatusResult.RoomStatus::getCalDate))
                                .collect(Collectors.toList()));
                    });
        });
        return list;
    }

    private List<GetHotelRoomStatusResult.RoomStatus> getCNBERoomstatusList(List<HtPriceCalendarCnbe> htPriceCalendarCnbes,
                                                                            List<HtPricePolicy> htPricePolicies,
                                                                            Map<String, List<HtPricePolicyRange>> htPricePolicyRangeMap) {
        return htPriceCalendarCnbes.stream()
                .flatMap(htCnbePriceCalendar -> htPricePolicies.stream()
                        .map(htPricePolicy -> {
                            GetHotelRoomStatusResult.RoomStatus roomStatus = GetHotelRoomStatusResult
                                    .RoomStatus.builder().build();
                            BeanUtil.copyProperties(htCnbePriceCalendar, roomStatus);
                            List<HtPricePolicyRange> htPricePolicyRanges
                                    = htPricePolicyRangeMap.get(htPricePolicy.getPricePolicyId());
                            updateRoomStatus(null, roomStatus, htPricePolicy, htPricePolicyRanges);
                            if (StringUtils.isBlank(roomStatus.getCancelRule())) {
                                roomStatus.setCancelRule("不可取消");
                            }
                            return roomStatus;
                        }))
                .sorted(Comparator.comparing(GetHotelRoomStatusResult.RoomStatus::getPricePolicyCode)
                        .thenComparing(GetHotelRoomStatusResult.RoomStatus::getBreakfastCount)
                        .thenComparing(GetHotelRoomStatusResult.RoomStatus::getCalDate))
                .collect(Collectors.toList());
    }

    private List<GetHotelRoomStatusResult.RoomStatus> getRezenRoomstatusList(List<HtPriceCalendarRezen> htPriceCalendarRezens,
                                                                             List<HtPricePolicy> htPricePolicies,
                                                                             Map<String, List<HtPricePolicyRange>> htPricePolicyRangeMap) {
        return htPriceCalendarRezens.stream()
                .flatMap(htRezenPriceCalendar -> htPricePolicies.stream()
                        .filter(htPricePolicy -> htRezenPriceCalendar.getBreakfastCount()
                                .equals(htPricePolicy.getBreakfastCount())
                                && htRezenPriceCalendar.getCancelPolicyType() != null
                                && htRezenPriceCalendar.getCancelPolicyType() == 4
                                && htRezenPriceCalendar.getSourceId().equals(htPricePolicy.getSourceId()))
                        .map(htPricePolicy -> {
                            GetHotelRoomStatusResult.RoomStatus roomStatus = GetHotelRoomStatusResult
                                    .RoomStatus.builder().build();
                            BeanUtil.copyProperties(htRezenPriceCalendar, roomStatus);
                            List<HtPricePolicyRange> htPricePolicyRanges
                                    = htPricePolicyRangeMap.get(htPricePolicy.getPricePolicyId());
                            updateRoomStatus(null, roomStatus, htPricePolicy, htPricePolicyRanges);
                            if (StringUtils.isBlank(roomStatus.getCancelRule())) {
                                roomStatus.setCancelRule("不可取消");
                            }
                            return roomStatus;
                        }))
                .sorted(Comparator.comparing(GetHotelRoomStatusResult.RoomStatus::getPricePolicyCode)
                        .thenComparing(GetHotelRoomStatusResult.RoomStatus::getBreakfastCount)
                        .thenComparing(GetHotelRoomStatusResult.RoomStatus::getCalDate))
                .collect(Collectors.toList());
    }

    private List<GetHotelRoomStatusResult.RoomStatus> getAtourRoomstatusList(List<HtPriceCalendarAtour> htPriceCalendarAtours,
                                                                             List<HtPricePolicy> htPricePolicies,
                                                                             Map<String, List<HtPricePolicyRange>> htPricePolicyRangeMap) {
        return htPriceCalendarAtours.stream()
                .flatMap(HtAtourPriceCalendar -> htPricePolicies.stream()
                        .filter(htPricePolicy -> HtAtourPriceCalendar.getSourceId()
                                .equals(htPricePolicy.getSourceId()))
                        .map(htPricePolicy -> {
                            GetHotelRoomStatusResult.RoomStatus roomStatus = GetHotelRoomStatusResult
                                    .RoomStatus.builder().build();
                            BeanUtil.copyProperties(HtAtourPriceCalendar, roomStatus);
                            List<HtPricePolicyRange> htPricePolicyRanges
                                    = htPricePolicyRangeMap.get(htPricePolicy.getPricePolicyId());
                            updateRoomStatus(null, roomStatus, htPricePolicy, htPricePolicyRanges);
                            if (StringUtils.isBlank(roomStatus.getCancelRule())) {
                                roomStatus.setCancelRule("不可取消");
                            }
                            // 莹旅 特殊处理 库存小于5变0
                            if ("bjsyinglv".equals(htPricePolicy.getRouteId()) && roomStatus.getQuota() < 5) {
                                roomStatus.setQuota(0);
                            }
                            return roomStatus;
                        }))
                .sorted(Comparator.comparing(GetHotelRoomStatusResult.RoomStatus::getPricePolicyCode)
                        .thenComparing(GetHotelRoomStatusResult.RoomStatus::getBreakfastCount)
                        .thenComparing(GetHotelRoomStatusResult.RoomStatus::getCalDate))
                .collect(Collectors.toList());
    }

    private List<GetHotelRoomStatusResult.RoomStatus> getRoomstatusListHworld(List<HtPriceCalendarHworld> htPriceCalendarHworlds,
                                                                              List<HtPricePolicy> htPricePolicies,
                                                                              Map<String, List<HtPricePolicyRange>> htPricePolicyRangeMap) {
        return htPriceCalendarHworlds.stream()
                .flatMap(htPriceCalendarHworld -> htPricePolicies.stream()
                        .filter(htPricePolicy -> htPricePolicy.getSourceId().equals(htPriceCalendarHworld.getSourceId()))
                        .filter(htPricePolicy -> StringUtils.isBlank(htPricePolicy.getExtra())
                                || (Objects.equals(htPriceCalendarHworld.getProductId(), JsonUtil.toMap(htPricePolicy.getExtra()).get("rateCode"))))
                        .map(htPricePolicy -> {
                            GetHotelRoomStatusResult.RoomStatus roomStatus = GetHotelRoomStatusResult.RoomStatus.builder().build();
                            BeanUtil.copyProperties(htPriceCalendarHworld, roomStatus);
                            List<HtPricePolicyRange> htPricePolicyRanges =
                                    htPricePolicyRangeMap.get(htPricePolicy.getPricePolicyId());
                            if (StringUtils.isBlank(roomStatus.getCancelRule())) {
                                roomStatus.setCancelRule("不可取消");
                            }
                            updateRoomStatus(null, roomStatus, htPricePolicy, htPricePolicyRanges);
                            return roomStatus;
                        }))
                .sorted(Comparator.comparing(GetHotelRoomStatusResult.RoomStatus::getPricePolicyCode)
                        .thenComparing(GetHotelRoomStatusResult.RoomStatus::getCalDate))
                .collect(Collectors.toList());
    }

    private List<GetHotelRoomStatusResult.RoomStatus> getRoomstatusListHmein(List<HtPriceCalendarHmein> htPriceCalendarHmeins,
                                                                             List<HtPricePolicy> htPricePolicies,
                                                                             Map<String, List<HtPricePolicyRange>> htPricePolicyRangeMap) {
        return htPriceCalendarHmeins.stream()
                .flatMap(htPriceCalendarHmein -> htPricePolicies.stream()
                        .filter(htPricePolicy -> htPricePolicy.getSourceId().equals(htPriceCalendarHmein.getSourceId()))
                        .map(htPricePolicy -> {
                            GetHotelRoomStatusResult.RoomStatus roomStatus = GetHotelRoomStatusResult.RoomStatus.builder().build();
                            BeanUtil.copyProperties(htPriceCalendarHmein, roomStatus);
                            List<HtPricePolicyRange> htPricePolicyRanges =
                                    htPricePolicyRangeMap.get(htPricePolicy.getPricePolicyId());
                            if (StringUtils.isBlank(roomStatus.getCancelRule())) {
                                roomStatus.setCancelRule("不可取消");
                            }
                            updateRoomStatus(null, roomStatus, htPricePolicy, htPricePolicyRanges);
                            return roomStatus;
                        }))
                .sorted(Comparator.comparing(GetHotelRoomStatusResult.RoomStatus::getPricePolicyCode)
                        .thenComparing(GetHotelRoomStatusResult.RoomStatus::getCalDate))
                .collect(Collectors.toList());
    }

    private List<GetHotelRoomStatusResult.RoomStatus> getRoomstatusListGreentree(List<HtPriceCalendarGreentree> htPriceCalendarGreentrees,
                                                                                 List<HtPricePolicy> htPricePolicies,
                                                                                 Map<String, List<HtPricePolicyRange>> htPricePolicyRangeMap) {
        return htPriceCalendarGreentrees.stream()
                .flatMap(htPriceCalendarGreentree -> htPricePolicies.stream()
                        .filter(htPricePolicy -> htPricePolicy.getSourceId().equals(htPriceCalendarGreentree.getSourceId()))
                        .map(htPricePolicy -> {
                            GetHotelRoomStatusResult.RoomStatus roomStatus = GetHotelRoomStatusResult.RoomStatus.builder().build();
                            BeanUtil.copyProperties(htPriceCalendarGreentree, roomStatus);
                            List<HtPricePolicyRange> htPricePolicyRanges =
                                    htPricePolicyRangeMap.get(htPricePolicy.getPricePolicyId());
                            if (StringUtils.isBlank(roomStatus.getCancelRule())) {
                                roomStatus.setCancelRule("不可取消");
                            }
                            updateRoomStatus(null, roomStatus, htPricePolicy, htPricePolicyRanges);
                            return roomStatus;
                        }))
                .sorted(Comparator.comparing(GetHotelRoomStatusResult.RoomStatus::getPricePolicyCode)
                        .thenComparing(GetHotelRoomStatusResult.RoomStatus::getCalDate))
                .collect(Collectors.toList());
    }

    /**
     * 更新房态数据（价格、早餐等）
     */
    private void updateRoomStatus(String hotelId, GetHotelRoomStatusResult.RoomStatus roomStatus,
                                  HtPricePolicy htPricePolicy,
                                  List<HtPricePolicyRange> htPricePolicyRanges) {
        HtPricePolicyRange htPricePolicyRange = htPricePolicyRanges.stream()
                .filter(e -> roomStatus.getPurchasePrice().compareTo(e.getRangePriceMin()) >= 0)
                .filter(e -> roomStatus.getPurchasePrice().compareTo(e.getRangePriceMax()) < 0)
                .findAny()
                .orElse(null);
        roomStatus.setPricePolicyCode(htPricePolicy.getPricePolicyCode());
        roomStatus.setPricePolicyName(htPricePolicy.getPricePolicyName());
        if (htPricePolicy.getBreakfastCount() != null) {
            roomStatus.setBreakfastCount(htPricePolicy.getBreakfastCount());
        }
        if (htPricePolicyRange != null) {
            BigDecimal raisedPurchasePrice = roomStatus.getPurchasePrice()
                    .multiply(htPricePolicyRange.getMarkupRate().add(BigDecimal.valueOf(1)))
                    .add(htPricePolicyRange.getMarkupFixed());
            if (StringUtils.isNotBlank(hotelId) && hotelId.startsWith(SourceEnum.HMEIN.getCode())) {
                raisedPurchasePrice = raisedPurchasePrice.setScale(0, BigDecimal.ROUND_HALF_UP);
            }
            roomStatus.setPurchasePrice(raisedPurchasePrice);
        } else {
            roomStatus.setPurchasePrice(null);
        }
    }
}
